﻿using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace S7Cat.Tools
{
    /// <summary>
    /// 表示基于ArrayPool的BufferWriter
    /// </summary>
    [DebuggerDisplay("WrittenCount = {WrittenCount}")]
    internal sealed class ArrayPoolBufferWriter<T> : IBufferWriter<T>, IDisposable
    {
        private T[] buffer;
        private bool disposed = false;
        private const int defaultSizeHint = 256;

        /// <summary>
        /// 获取已数入的数据长度
        /// </summary>
        public int WrittenCount { get; private set; } = 0;

        /// <summary>
        /// 获取已数入的数据
        /// </summary>
        public ReadOnlySpan<T> WrittenSpan => this.buffer.AsSpan(0, this.WrittenCount);

        /// <summary>
        /// 获取已数入的数据(可变)
        /// </summary>
        public Span<T> Span => this.buffer.AsSpan(0, this.WrittenCount);

        /// <summary>
        /// 获取已数入的数据(可变)
        /// </summary>
        public Memory<T> Memory => this.buffer.AsMemory(0, this.WrittenCount);

        /// <summary>
        /// 获取已数入的数据
        /// </summary>
        public ReadOnlyMemory<T> WrittenMemory => this.buffer.AsMemory(0, this.WrittenCount);

        /// <summary>
        /// 获取已数入的数据
        /// </summary>
        /// <returns></returns>
        public ArraySegment<T> WrittenSegment => new(this.buffer, 0, this.WrittenCount);

        /// <summary>
        /// 获取容量
        /// </summary>
        public int Capacity => this.buffer.Length;

        /// <summary>
        /// 获取剩余容量
        /// </summary>
        public int FreeCapacity => this.buffer.Length - this.WrittenCount;


        /// <summary>
        /// 基于ArrayPool的BufferWriter
        /// </summary>
        /// <param name="initialCapacity">初始容量</param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public ArrayPoolBufferWriter(int initialCapacity)
        {
            ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initialCapacity);
            this.buffer = ArrayPool<T>.Shared.Rent(initialCapacity);
        }

        /// <summary>
        /// 清除数据
        /// </summary>
        public void Clear()
        {
            this.buffer.AsSpan(0, this.WrittenCount).Clear();
            this.WrittenCount = 0;
        }

        /// <summary>
        /// 设置向前推进
        /// </summary>
        /// <param name="count"></param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public void Advance(int count)
        {
            if (count < 0 || this.WrittenCount > this.buffer.Length - count)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            this.WrittenCount += count;
        }

        /// <summary>
        /// 返回用于写入数据的Memory
        /// </summary>
        /// <param name="sizeHint">意图大小</param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <returns></returns>
        public Memory<T> GetMemory(int sizeHint = 0)
        {
            this.CheckAndResizeBuffer(sizeHint);
            return this.buffer.AsMemory(this.WrittenCount);
        }

        /// <summary>
        /// 返回用于写入数据的Span
        /// </summary>
        /// <param name="sizeHint">意图大小</param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <returns></returns>
        public Span<T> GetSpan(int sizeHint = 0)
        {
            this.CheckAndResizeBuffer(sizeHint);
            return buffer.AsSpan(this.WrittenCount);
        }

        /// <summary>
        /// 返回用于写入数据的大小确定的Span
        /// </summary>
        /// <param name="sizeHint"></param>
        /// <returns></returns>
        public Span<T> GetFixedSpan(int sizeHint = 0)
        {
            this.CheckAndResizeBuffer(sizeHint);
            return buffer.AsSpan(this.WrittenCount, sizeHint);
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="value"></param>
        public void Write(T value)
        {
            this.GetSpan(1)[0] = value;
            this.WrittenCount += 1;
        }

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="value">值</param> 
        public void Write(ReadOnlySpan<T> value)
        {
            if (value.IsEmpty == false)
            {
                value.CopyTo(this.GetSpan(value.Length));
                this.WrittenCount += value.Length;
            }
        }

        public T[] ToArray()
        {
            var array = new T[this.WrittenCount];
            Array.Copy(this.buffer, array, this.WrittenCount);
            return array;
        }

        /// <summary>
        /// 检测和扩容
        /// </summary>
        /// <param name="sizeHint"></param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void CheckAndResizeBuffer(int sizeHint)
        {
            if (sizeHint < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(sizeHint));
            }

            if (sizeHint == 0)
            {
                sizeHint = defaultSizeHint;
            }

            if (sizeHint > this.FreeCapacity)
            {
                int currentLength = this.buffer.Length;
                var growBy = Math.Max(sizeHint, currentLength);
                var newSize = checked(currentLength + growBy);

                var newBuffer = ArrayPool<T>.Shared.Rent(newSize);
                Array.Copy(this.buffer, newBuffer, this.WrittenCount);

                ArrayPool<T>.Shared.Return(this.buffer);
                this.buffer = newBuffer;
            }
        }

        /// <summary>
        /// 将对象进行回收
        /// </summary>
        public void Dispose()
        {
            if (this.disposed == false)
            {
                ArrayPool<T>.Shared.Return(this.buffer);
                GC.SuppressFinalize(this);
            }
            this.disposed = true;
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        ~ArrayPoolBufferWriter()
        {
            this.Dispose();
        }
    }
}
